%{

  int yylex ();
  int yyerror ();

#include <stdlib.h>
#include "str_list.h"
#include "declarator_list.h"
#include "tab_sym.h"
#include <stdio.h>
#include <string.h>


  
  struct table_symbole *global_tab;
  struct table_symbole *current_tab;

  struct statement{
    char * cmd;
    char * val;
    struct table_symbole *table;
  };

  struct statement_list{
    struct statement *statement;
    struct statement_list * next;
  };
   
  struct statement_list *statement_list_vide(){
  struct statement_list *l = malloc(sizeof(struct statement_list));
  l->statement = NULL;
  return l;
}

struct statement_list *statement_list_ajouter(struct statement_list*l,struct statement* s){
  struct statement_list *tmp = l;
  while (tmp->next != NULL){
    tmp = tmp->next;
  }
  tmp->next->statement = s;
  tmp->next->next = NULL;
  return l; 
}
  

  struct pile_registre{
    int _i;
    char _alloc[32];
  };
  
 struct pile_registre pile;
 void registre_init(){
   int i=-1;
   for(;i<32;i++)
     pile._alloc[i] = 0;
 }
 int registre_push(){
   int i=0;
   while(pile._alloc[i] == 1)
     i++;
   if(i>=32)
     return 1; // erreur plus de place
   pile._alloc[i]=1;
   pile._i = i;
   return 0;
 }
 void registre_pop(char * s){
   s++;
   pile._alloc[atoi(s)]=0;
 }
 char * registre_get(){
   char * s = malloc(21);
   sprintf(s,"e%d",pile._i);
   return s;
 }


%}


%token<str> IDENTIFIER CONSTANT 
%token INC_OP DEC_OP LE_OP GE_OP EQ_OP NE_OP
%token AND_OP OR_OP
%token SUB_ASSIGN MUL_ASSIGN ADD_ASSIGN
%token TYPE_NAME
%union {
  char *str;
  struct ret *ret;
  struct str_list *list;
  struct declarator * decl;
  struct table_symbole * tab;
  struct symb *symb;
  struct declarator_list *decl_list;
  struct statement *statement;
  struct statement_list * st_list;
  int i;
}
%token INT FLOAT VOID

%token  IF ELSE GOTO RETURN

%type<i> type_name 


%type<str> '+' '-' assignment_operator unary_operator
%type<ret> primary_expression postfix_expression unary_expression expression comparison_expression

%type<list> argument_expression_list

%type<decl> declarator parameter_declaration

%type<decl_list> declaration declarator_list parameter_list declaration_list

%type<st_list> statement_list

%type<statement> compound_statement statement labeled_statement jump_statement expression_statement selection_statement external_declaration function_definition program


%start program
%%
primary_expression
: IDENTIFIER
{
  int offset = get_offset(current_tab, $1);
  $$->cmd = malloc(sizeof(char)*1000);
  registre_push();
  sprintf($$->cmd, "mov %d(%%ebp), %s", offset, registre_get()); 
  $$->val = registre_get(); 
}
| CONSTANT
{
  $$->cmd = "\n";
  sprintf($$->val, "$%d", atoi($1));
}
| IDENTIFIER '(' ')'
{
  $$->cmd = malloc(sizeof(char)*1000);
  sprintf($$->cmd,"call _%s",$1);
  $$->val = "%eax";
}
| IDENTIFIER '(' argument_expression_list ')'
{
  struct str_list *tmp = $3;
  while(tmp->next != NULL){
    $$->cmd = malloc(sizeof(char)*1000);
    sprintf($$->cmd,"%s\n", tmp->ret->cmd);
    tmp = tmp->next;
  }
  sprintf($$->cmd,"call _%s", $1);
  $$->val = "%eax";
}
| IDENTIFIER INC_OP
{
  int offset = get_offset(current_tab, $1);
  $$->cmd = malloc(sizeof(char)*1000);
  registre_push();

  sprintf($$->cmd, "inc %d(%%ebp), \nmov %d(%%ebp), %s", offset, offset, registre_get());
  $$->val = registre_get();
}
| IDENTIFIER DEC_OP
{
  int offset = get_offset(current_tab, $1);
  $$->cmd = malloc(sizeof(char)*1000);
  registre_push();
  sprintf($$->cmd, "sub %d(%%ebp), $1\nmov %d(%%ebp), %s", offset, offset, registre_get());
  $$->val = registre_get();
}
;

postfix_expression
: primary_expression
{
memcpy(&($$),&($1),sizeof($1));
}
| postfix_expression '[' expression ']'
{
  $$->val = "";
  $$->cmd = "";
}
;

argument_expression_list
: primary_expression
{
  struct str_list *l = str_list_vide();
  $$ = str_list_ajouter(l, $1);
}
| argument_expression_list ',' primary_expression
{
  $$ = str_list_ajouter($1, $3);
}
;

unary_expression
: postfix_expression
{
  memcpy(&($$),&($1),sizeof($1));
}
| INC_OP unary_expression
{
 $$->cmd = malloc(sizeof(char)*1000);
  sprintf($$->cmd, "%s\ninc %s\n", $2->cmd, $2->val);
  $$->val = $2->val;
}
| DEC_OP unary_expression
{
 $$->cmd = malloc(sizeof(char)*1000);
  sprintf($$->cmd, "%s\ndec %s\n", $2->cmd, $2->val);
  $$->val = $2->val;
}
| unary_operator unary_expression
{
  if(strcmp($1, "-") == 0){
 $$->cmd = malloc(sizeof(char)*1000);
    sprintf($$->cmd, "%s\nneg %s\n", $2->cmd, $2->val);   
    $$->val = $2->val;
  }
  else
    memcpy(&($$),&($2),sizeof($2));
}
;

unary_operator
: '+'
{
  $$ = $1;
}
| '-'
{
  $$ = $1;
}
;

comparison_expression
: unary_expression
{
 $$->cmd = malloc(sizeof(char)*1000);
  sprintf($$->cmd,"%s\ncmp %s, $0\njne endif\n", $1->cmd, $1->val);
}
| primary_expression '<' primary_expression
{
 $$->cmd = malloc(sizeof(char)*1000);
  sprintf($$->cmd,"%s\n%s\ncmp %s, %s\njnl endif\n", $1->cmd, $3->cmd, $1->val, $3->val);
}
| primary_expression '>' primary_expression
{
 $$->cmd = malloc(sizeof(char)*1000);
  sprintf($$->cmd,"%s\n%s\ncmp %s, %s\njng endif\n", $1->cmd, $3->cmd, $1->val, $3->val);
}
| primary_expression LE_OP primary_expression
{
 $$->cmd = malloc(sizeof(char)*1000);
  sprintf($$->cmd,"%s\n%s\ncmp %s, %s\njnle endif\n", $1->cmd, $3->cmd, $1->val, $3->val);
}
| primary_expression GE_OP primary_expression
{
 $$->cmd = malloc(sizeof(char)*1000);
  sprintf($$->cmd,"%s\n%s\ncmp %s, %s\njnge endif\n", $1->cmd, $3->cmd, $1->val, $3->val);
}
| primary_expression EQ_OP primary_expression
{
 $$->cmd = malloc(sizeof(char)*1000);
  sprintf($$->cmd,"%s\n%s\ncmp %s, %s\njne endif\n", $1->cmd, $3->cmd, $1->val, $3->val);
}
| primary_expression NE_OP primary_expression
{
 $$->cmd = malloc(sizeof(char)*1000);
  sprintf($$->cmd,"%s\n%s\ncmp %s, %s\nje endif\n", $1->cmd, $3->cmd, $1->val, $3->val);
}
;

expression
:unary_expression assignment_operator unary_expression
{
 $$->cmd = malloc(sizeof(char)*1000);
  sprintf($$->cmd, "%s\n %s\n%s %s,%s\n", $1->cmd,$3->cmd, $2, $1->val, $3->val);
  $$->val = $1->val;
} 
| unary_expression
{
    memcpy(&($$),&($1),sizeof($1));
}
;

assignment_operator
: '='
{
  $$ = "mov";
}
| MUL_ASSIGN
{
  $$="mul";
    }
| ADD_ASSIGN
{
  $$="add";
}
| SUB_ASSIGN
{
  $$="sub";
}
;

declaration
: type_name declarator_list ';' 
{
  struct declarator_list* tmp = $2;
  while(tmp->next != NULL){
    set_declarator_type(tmp->decl, $1);
    tmp = tmp->next;
  }
  $$ = $2;
}  
;

declarator_list
: declarator
{
  struct declarator_list *l = declarator_list_vide();

declarator_list_ajouter(l, $1);
$$ =l;
}
| declarator_list ',' declarator
{
  declarator_list_ajouter($1, $3);
  $$=$1;
}
;

type_name
: INT
{
  $$ = 5;
}
| VOID
{
  $$ = 0;
}
| FLOAT
{
  $$ = 6;
}
;

declarator
: IDENTIFIER
{
  struct declarator * decl = malloc(sizeof(* decl));
  decl->mode = 1;
  sprintf(decl->id, "%s", $1);
  $$=decl;
}
| '(' declarator ')' //useless
{
  $$=NULL;
}
| declarator '[' CONSTANT ']'
{
  struct declarator * decl = malloc(sizeof(* decl));
  decl->taille = malloc(sizeof(struct taille));
  decl->taille->taille = 0;
  decl->mode = 2;
  ajouter_taille(decl->taille, atoi($3) );
  sprintf(decl->id, "%s", $1->id);
  $$=decl;
}
  
| declarator '[' ']' //useless
  /*{
  struct declarator * decl = malloc(sizeof(* decl));
  decl->mode = 3;
  sprintf(decl->id, "%s", $1);
  $$=decl;
  }*/
| declarator '(' parameter_list ')'
{
  struct declarator * decl = malloc(sizeof(* decl));
  decl->mode = 3;
  declarator_ajouter_tab(decl, $3);
  sprintf(decl->id, "%s", $1->id);
  $$=decl;
}
| declarator '(' ')'
{
  struct declarator * decl = malloc(sizeof(* decl));
  decl->mode = 4;
  sprintf(decl->id, "%s", $1->id);
  $$=decl;
}
;


parameter_list 
: parameter_declaration
{
  struct declarator_list *l = declarator_list_vide();
  declarator_list_ajouter(l, $1);
  $$ = l;
}
| parameter_list ',' parameter_declaration
{
  declarator_list_ajouter($1, $3);
  $$ = $1;
}
;

parameter_declaration
: type_name declarator
{
  set_declarator_type($2, $1);
  $$=$2;
}
;

statement
: labeled_statement
{
memcpy(&($$),&($1),sizeof($1));
}
| compound_statement
{
memcpy(&($$),&($1),sizeof($1));
}
| expression_statement
{
memcpy(&($$),&($1),sizeof($1));
}
| selection_statement
{
memcpy(&($$),&($1),sizeof($1));
}
| jump_statement
{
memcpy(&($$),&($1),sizeof($1));
}
;

labeled_statement
: IDENTIFIER ':' statement
{
 $$->cmd = malloc(sizeof(char)*1000);
  sprintf($$->cmd, "%s: %s\n", $1, $3->cmd);
  $$->val = $3->val;
  $$->table = $3->table;
}
;

compound_statement
: '{' '}'
{
 $$->cmd = malloc(sizeof(char)*1000);
  sprintf($$->cmd, " ");
  $$->table = tab_symb_vide(NULL, LOCAL);
}
| '{' statement_list '}'
 {
   memcpy(&($$),&($2),sizeof($2));
}
| '{' declaration_list statement_list '}'
{
  struct table_symbole * t = tab_symb_vide(NULL, LOCAL);
  struct declarator_list* tmp = $2;
  while(tmp->next != NULL){
    if (tmp->decl->mode == 3 || tmp->decl->mode == 4)
      ajouter_func(t, tmp->decl);
    else if (tmp->decl->mode == 1 || tmp->decl->mode ==2) 
      ajouter_var(t, tmp->decl);
    tmp = tmp->next;
  }
  struct statement_list *s = $3;
  while (s->next != NULL){
  
    s = s->next;
  }
  
  struct statement_list *list = $3;
 $$->cmd = malloc(sizeof(char)*1000);
  sprintf($$->cmd, " ");
  while(list->next != NULL){
    sprintf($$->cmd,"%s\n%s\n", $$->cmd, list->statement->cmd);
    list->statement->table->parent = t;
    list = list->next;
  } 
  $$->table = t;
}
;

declaration_list
: declaration
| declaration_list declaration
{  
  declarator_list_concat($1, $2);
  $$ = $1;
}
;

statement_list
: statement
{
  struct statement_list *l = statement_list_vide();
  $$ = statement_list_ajouter(l, $1);
}
| statement_list statement
{
  $$ = statement_list_ajouter($1, $2);
}
;

expression_statement
: ';'
{
 $$->cmd = malloc(sizeof(char)*1000);
  $$->cmd=" ";
  $$->val=" ";
}
| expression ';'
{
 memcpy(&($$),&($1),sizeof($1));
}
;

selection_statement
: IF '(' comparison_expression ')' statement
{
 $$->cmd = malloc(sizeof(char)*1000);
  sprintf($$->cmd,"if: %s\nthen %s\njmp endif\endif:\n", $3->cmd, $5->cmd);
  $$->table = $5->table;
}
;

jump_statement
: GOTO IDENTIFIER ';'
{
 $$->cmd = malloc(sizeof(char)*1000);
  sprintf($$->cmd, "jmp %s\n", $2);
}
| RETURN ';'
{
 $$->cmd = malloc(sizeof(char)*1000);
  sprintf($$->cmd, "ret");
}
| RETURN expression ';'
{
  $$->cmd = malloc(sizeof(char)*1000);
  sprintf($$->cmd, "mov %%eax %s\nret\n", $2->val);
}
;

program
: external_declaration
{
  printf("%s\n", $1->cmd);
}
| program external_declaration
{
  printf("%s\n", $2->cmd);
}
;

external_declaration
: function_definition
{
    memcpy(&($$),&($1),sizeof($1));
}
| declaration
{
  struct declarator_list * tmp = $1;
  while(tmp->next != NULL){
    if(tmp->decl->mode == 1 || tmp->decl->mode == 2)
      ajouter_var(global_tab, tmp->decl);
    tmp = tmp->next;
    }
}
;

function_definition
: type_name declarator compound_statement
{
   set_declarator_type($2, $1);
  $3->table->portee = LOCAL;
  $3->table->parent = global_tab;
  $2->param_list->parent = global_tab;
  struct declarator *tmp = $2;
  if (tmp->mode == 3 || tmp->mode == 4)
      ajouter_func(global_tab, tmp);

  memcpy(&($$),&($3),sizeof($3));
}
;

%%
#include <stdio.h>

extern char yytext[];
extern int column;
extern int yylineno;
extern FILE *yyin;

char *file_name = NULL;

int yyerror (char *s) {
    fflush (stdout);
    fprintf (stderr, "%s:%d:%d: %s\n", file_name, yylineno, column, s);
    return 0;
}


int main (int argc, char *argv[]) {
 global_tab = tab_symb_vide(NULL, GLOBAL);
 current_tab = global_tab;
  registre_init();
    FILE *input = NULL;
    if (argc==2) {
	input = fopen (argv[1], "r");
	file_name = strdup (argv[1]);
	if (input) {
	    yyin = input;
	}
	else {
	    fprintf (stderr, "Could not open %s\n", argv[1]);
	    return 1;
	}
    }
    else {
	fprintf (stderr, "%s: error: no input file\n", *argv);
	return 1;
    }
    yyparse ();
    free (file_name);
    return 0;
}
